home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 68K / Demo / www / wwwlib.py < prev    next >
Text File  |  1996-05-20  |  9KB  |  318 lines

  1. # WWW protocol interface
  2.  
  3. # Default home page
  4. WWW_HOME = 'http://info.cern.ch:80/default.html'
  5.  
  6. # Exception raised when an address leads nowhere
  7. BadAddress = 'wwwlib.BadAddress'
  8.  
  9. # Parse an "anchoraddr", return (scheme, host, port, path, search, anchor)
  10. #
  11. # Note that search and anchor *include* the leading '?' or '#',
  12. # but host and port don't include the leading '//' or ':' and scheme
  13. # excludes the trailing ':'.
  14. #
  15. # (Some address forms don't support all parts, and some have
  16. # additional parts; yet this does something useful for all formats.)
  17. #
  18. import regex
  19. schemeprog = regex.compile('^[^/:?#]*:')
  20. hostportprog = regex.compile('//[^/:?#]*\(:[0-9]*\)?')
  21. portprog = regex.compile(':[0-9]*')
  22. searchprog = regex.compile('\?[^#]*')
  23. anchorprog = regex.compile('#.*')
  24. #
  25. def parse_addr(anchoraddr):
  26.     i = anchorprog.search(anchoraddr)
  27.     if i >= 0:
  28.         anchor = anchoraddr[i:]
  29.         docaddr = anchoraddr[:i]
  30.     else:
  31.         anchor = ''
  32.         docaddr = anchoraddr
  33.     #
  34.     i = schemeprog.match(docaddr)
  35.     if i >= 0:
  36.         scheme = docaddr[:i-1]
  37.         path = docaddr[i:]
  38.     else:
  39.         scheme = ''
  40.         path = docaddr
  41.     #
  42.     if path[:2] == '//':
  43.         i = hostportprog.match(path)
  44.         hostport = path[2:i]
  45.         path = path[i:]
  46.     else:
  47.         hostport = ''
  48.     #
  49.     i = portprog.search(hostport)
  50.     if i >= 0:
  51.         host = hostport[:i]
  52.         port = hostport[i+1:]
  53.     else:
  54.         host = hostport
  55.         port = ''
  56.     #
  57.     i = searchprog.search(path)
  58.     if i >= 0:
  59.         search = path[i:]
  60.         path = path[:i]
  61.     else:
  62.         search = ''
  63.     #
  64.     return scheme, host, port, path, search, anchor
  65.  
  66. # Convert a (scheme, ..., anchor) tuple back into an address string
  67. def unparse_addr(scheme, host, port, path, search, anchor):
  68.     a = ''
  69.     if scheme:
  70.         a = a + scheme + ':'
  71.     if host or port:
  72.         a = a + '//' + host
  73.         if port:
  74.             a = a + ':' + port
  75.         if path and path[0] <> '/':
  76.             a = a + '/'
  77.     a = a + path
  78.     if search:
  79.         if search[0] <> '?':
  80.             search = '?' + search
  81.         a = a + search
  82.     if anchor:
  83.         if anchor[0] <> '#':
  84.             anchor = '#' + anchor
  85.         a = a + anchor
  86.     return a
  87.  
  88. # Turn a relative address into a full address given the parent's address
  89. def full_addr(parent, addr, *isindex):
  90.     pscheme, phost, pport, ppath, psearch, panchor = parse_addr(parent)
  91.     if not pscheme: pscheme = 'file'
  92.     if phost[-1:] == '.': phost = phost[:-1]
  93.     if pscheme == 'http' and pport in ('80', '2784') or pscheme == 'file':
  94.         pport = '' # XXX
  95.     scheme, host, port, path, search, anchor = parse_addr(addr)
  96.     if not scheme: scheme = pscheme
  97.     if scheme == pscheme:
  98.         if not host:
  99.             host = phost
  100.             if not port: port = pport
  101.         if host[-1:] == '.':
  102.             host = host[:-1]
  103.         if not path:
  104.             path = ppath
  105.         elif path[0] <> '/' and '/' in ppath:
  106.             import posixpath
  107.             i = len(ppath)
  108.             while i > 0 and ppath[i-1] <> '/':
  109.                 i = i-1
  110.             path = ppath[:i] + path
  111.             path = posixpath.normpath(path)
  112.     if scheme == 'http' and port in ('80', '2784'): port = '' # XXX
  113.     full = unparse_addr(scheme, host, port, path, search, anchor)
  114.     return full
  115.  
  116. # Get a document, given its (full) address
  117. # (XXX This doesn't support the full range of address schemes yet,
  118. # and of course the "telnet" scheme needs special treatment anyway.)
  119. def get_document(addr):
  120.     scheme, host, port, path, search, anchor = parse_addr(addr)
  121.     #
  122.     if scheme == 'http':
  123.         import httplib, socket, string
  124.         if port:
  125.             try:
  126.                 port = string.atoi(port)
  127.             except string.atoi_error:
  128.                 raise BadAddress, 'bad port number'
  129.         if search:
  130.             if search[0] <> '?':
  131.                 search = '?' + search
  132.             path = path + search
  133.         try:
  134.             data = httplib.get_htmlfile(host, port, path)
  135.         except socket.error, msg:
  136.             if type(msg) <> type(''): msg = `msg`
  137.             raise BadAddress, msg
  138.         # Interpret a short single-line document with no tags
  139.         # as an error message
  140.         if len(data) <= 100 and '<' not in data and \
  141.             '\n' not in data[:-1]:
  142.             if data[-2:] == '\r\n': data = data[:-2]
  143.             elif data[-1:] == '\n': data = data[:-1]
  144.             raise BadAddress, data
  145.         return data
  146.     #
  147.     if (scheme == 'file' or scheme == 'ftp' or scheme == ''):
  148.         import socket
  149.         isdir =  0
  150.         if host <> '' and socket.gethostbyname(host) <> \
  151.                      socket.gethostbyname(socket.gethostname()):
  152.             import ftplib
  153.             class C:
  154.                 def add(self, x):
  155.                     self.data = self.data + (x + '\n')
  156.             x = C()
  157.             x.data = ''
  158.             try:
  159.                 ftp = ftplib.FTP(host)
  160.             except ftplib.all_errors:
  161.                 raise BadAddress, \
  162.                     'ftp connect to ' + host + ' failed'
  163.             try:
  164.                 ftp.login() # user anonymous, passwd user@host
  165.             except ftplib.all_errors:
  166.                 raise BadAddress, \
  167.                     'ftp login on ' + host + ' failed'
  168.             # First try retrieving as a file, then listing
  169.             # as a directory
  170.             try:
  171.                 ftp.retrlines('RETR ' + path, x.add)
  172.                 data = x.data
  173.             except ftplib.error_perm:
  174.                 try:
  175.                     ftp.voidcmd('CWD ' + path)
  176.                     names = ftp.nlst()
  177.                 except ftplib.all_errors:
  178.                     raise BadAddress, \
  179.                         'ftp can\'t find ' + path + \
  180.                         ' on ' + host
  181.                 data = names_to_html(host, path, names, 0)
  182.                 isdir = 1
  183.             except ftplib.all_errors:
  184.                 raise BadAddress, \
  185.                     'ftp retrieve ' + path + ' from ' + \
  186.                     host + ' failed'
  187.         else:
  188.             import os
  189.             if os.path.isdir(path):
  190.                 names = os.listdir(path)
  191.                 if os.curdir in names: names.remove(os.curdir)
  192.                 if os.pardir in names: names.remove(os.pardir)
  193.                 data = names_to_html(host, path, names, 1)
  194.                 isdir = 1
  195.             else:
  196.                 try:
  197.                     data = open(path, 'r').read()
  198.                 except IOError, msg:
  199.                     raise BadAddress, \
  200.                           'can\'t open local file ' + \
  201.                           `path` + ' : ' + `msg`
  202.         if not isdir and path[-5:] <> '.html':
  203.             data = '<PLAINTEXT>\n' + data
  204.         return data
  205.     #
  206.     if scheme == 'gopher':
  207.         import string
  208.         import gopherlib
  209.         if len(path) >= 2 and path[0] == '/':
  210.             gtype = path[1]
  211.             selector = path[2:]
  212.         else:
  213.             selector = ''
  214.             gtype = gopherlib.A_DIRECTORY
  215.         # Convert '%' escapes in selectors
  216.         while '%' in selector:
  217.             i = string.index(selector, '%')
  218.             xx = selector[i+1:i+3]
  219.             try:
  220.                 n = eval('0x' + xx)
  221.                 c = chr(n)
  222.             except:
  223.                 c = ''
  224.             selector = selector[:i] + c + selector[i+3:]
  225.         if gtype == gopherlib.A_FILE:
  226.             f = gopherlib.send_selector(selector, host, port)
  227.             lines = gopherlib.get_textfile(f)
  228.             lines.append('')
  229.             data = string.joinfields(lines, '\n')
  230.             return '<PLAINTEXT>\n' + data
  231.         if gtype == gopherlib.A_DIRECTORY:
  232.             f = gopherlib.send_selector(selector, host, port)
  233.             directory = gopherlib.get_directory(f)
  234.             return gopher_to_html(directory)
  235.         if gtype == gopherlib.A_HTML:
  236.             f = gopherlib.send_selector(selector, host, port)
  237.             lines = gopherlib.get_textfile(f)
  238.             lines.append('')
  239.             data = string.joinfields(lines, '\n')
  240.             return data
  241.         if gtype == gopherlib.A_INDEX:
  242.             if not search:
  243.                 return '<ISINDEX>\n' + \
  244.                        '<TITLE>Gopher index</TITLE>\n' + \
  245.                        '<H1>Enter search keywords</H1>\n'
  246.             words = string.splitfields(search, '+')
  247.             selector = selector + '\t' + string.join(words)
  248.             f = gopherlib.send_selector(selector, host, port)
  249.             directory = gopherlib.get_directory(f)
  250.             return gopher_to_html(directory)
  251.         raise BadAddress, 'unsupported gopher type ' + `gtype`
  252.     #
  253.     raise BadAddress, 'unsupported scheme ' + `scheme`
  254.  
  255. # Convert a Gopher directory to HTML
  256. def gopher_to_html(directory):
  257.     import string
  258.     import gopherlib
  259.     data = '<TITLE>Gopher menu</TITLE>\n<H1>Gopher menu</H1>\n<UL>\n'
  260.     for item in directory:
  261.         [gtype, userstring, selector, host, port] = item[:5]
  262.         if gtype == gopherlib.A_ERROR:
  263.             text = '<LI>' + userstring + '\n'
  264.         else:
  265.             # Convert spaces in selectors to '%' escapes
  266.             i = 0
  267.             while i < len(selector):
  268.                 c = selector[i]
  269.                 if c in ' <>&?#%':
  270.                     c = '%' + hex(0x100|ord(c))[-2:]
  271.                     selector = selector[:i] + c + \
  272.                        selector[i+1:]
  273.                 i = i + len(c)
  274.             text = '<LI><A HREF=gopher://' + host + ':' + port + \
  275.                 '/' + gtype + selector + '>' + \
  276.                 userstring + '</A>\n'
  277.         typestr = gopherlib.type_to_name(gtype)
  278.         text = text + typestr + '\n'
  279.         data = data + text
  280.     data = data + '</UL>\n'
  281.     return data
  282.  
  283.  
  284. # Convert a unix directory listing to HTML
  285. def names_to_html(host, path, names, local):
  286.     import string, os
  287.     prefix = '<LI><A HREF=file:'
  288.     if host:
  289.         prefix = prefix + '//' + host
  290.         if path[:1] <> '/': path = '/' + path
  291.     if not path: path = '.'
  292.     if host: header = 'Directory ' + path + ' on ' + host
  293.     else: header = 'Local directory ' + path
  294.     data = '<H1>' + header + '</H1>\n<DIR COMPACT>\n'
  295.     if path[-1:] <> '/': path = path + '/'
  296.     if path not in ('/', './'):
  297.         dirname = os.path.normpath(path + '../')
  298.         text = prefix + dirname + '>../</A>\n'
  299.         data = data + text
  300.     prefix = prefix + path
  301.     names.sort()
  302.     for name in names:
  303.         if local and os.path.isdir(path + name):
  304.             name = name + '/'
  305.         text = prefix + name + '>' + name + '</A>\n'
  306.         data = data + text
  307.     data = data + '</DIR>'
  308.     return data
  309.  
  310.  
  311. # Main program for simple testing
  312. def test():
  313.     import sys
  314.     if not sys.argv[1:]:
  315.         sys.argv.append(WWW_HOME)
  316.     for path in sys.argv[1:]:
  317.         print get_document(path)
  318.